﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using alib.Collections.ReadOnly;
using alib.Concurrency;
using alib.Debugging;
using alib.Enumerable;
using alib.String;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class Tfs
	{
#if false
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if DEBUG
		long _h = -1;
		public long TfsHash()
		{
			if (_h == -1)
				_h = TfsHash(TopEdge);
			return _h;
		}
#else
		public long TfsHash()
		{
			Nop.CodeCoverage();
			return TfsHash(TopEdge);
		}
#endif
		public long TfsHash(Edge e)
		{
			return e.Mark != 0 ? new TfsHashGatherer(this, e).Result : (long)((int)e.FlagsId ^ 0x12345678);
		}

		sealed class TfsHashGatherer : HashSet<FeatMark>
		{
			Tfs tfs;
			TypeMgr tm;
			long h = 0;

			public TfsHashGatherer(ArrayTfs tfs, Edge e)
			{
				this.tfs = tfs;
				this.tm = tfs.tm;
				_gather(e, 1);
			}

			void _gather(Edge e, int level)
			{
				foreach (int i_feat in fcm.rgrgfix_by_type[(int)(e.FlagsId & Edge.Flag.MultiIdMask)])
				{
					Edge ne;
					if (tfs.TryGetEdge(i_feat, e.Mark, out ne))
					{
						h += (((long)(uint)i_feat << 32) | (long)(uint)ne.FlagsId) * level;
						FeatMark fm = new FeatMark(i_feat, e.Mark);
						if (this.Add(fm))
							if ((ne.FlagsId & Edge.Flag.EtmNonBareType) != 0)
								_gather(ne, level + 1);
					}
				}
			}

			public long Result { get { return h; } }
		};
#endif

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public String ToPathList(bool f_marks = false)
		{
			return new PathLister(this, _top_edge, null, f_marks, null, false).ToString();
		}
		public String ToPathList(String label, bool f_marks = false)
		{
			return new PathLister(this, _top_edge, label, f_marks, null, false).ToString();
		}
		public String ToPathList(Edge edge, String label, bool f_marks = false, bool[] restrictor = null, bool f_remove_redundant_corefs = false)
		{
			return new PathLister(this, edge, label, f_marks, restrictor, f_remove_redundant_corefs).ToString();
		}

		public sealed class PathLister : List<String>, IAddRangeList<String>
		{
			Tfs tfs;
			TypeUtils tu;
			FeatMgr ftm;
			Stack<String> stack = new Stack<String>();
			Dictionary<Edge, int> d = null;
			int coref_tag = 0;
			bool f_marks;
			bool f_internal;
			bool[] restrictor = null;
			byte[] coref_tallies = null;

			public PathLister(TfsSlot ts, String label, bool f_marks, bool[] restrictor, bool f_remove_redundant_corefs)
				: this(ts, ts.Edge, label, f_marks, restrictor, f_remove_redundant_corefs)
			{
			}

			public PathLister(ITfsSlot ts, Edge e, String label, bool f_marks, bool[] restrictor, bool f_remove_redundant_corefs)	// HashSet<FeatMark> reachable = null, bool f_internal = false
			{
				this.tfs = ts.Tfs;
				this.tu = tfs.tu;
				this.ftm = tfs.ftm;
				this.f_marks = f_marks;
				this.f_internal = false;
				if (f_remove_redundant_corefs)
				{
					coref_tallies = new EdgeCounter(tfs).CorefTallies;
				}
				this.restrictor = restrictor;
				if (label != null)
					this.Add(String.Format(";;;;;;;;; ({0})", label));
				_list_paths(e);
			}

			void _list_paths(Edge e)
			{
				bool f_descend = true;
				Type t;
				if (tu.IsTopId(e))
				{
					t = tu.td.ΔTop;
					f_descend = false;
				}
				else
					t = tu.GetEdgeType(e.FlagsId);
				String s = String.Format("{0} {1}", stack.Reverse().StringJoin("."), t);
				if (e.FlagsId.IsSkolemValue())
					s += " " + TypeUtils.GetSkolemString(e.FlagsId);
				else if (e.FlagsId.IsIntegerValue())
					s += " " + e.FlagsId.GetIntegerValue().ToString();
				else if (e.FlagsId.IsStringValue())
					s += " " + tu.GetStringValue(e.FlagsId).DoubleQuote();
				bool f_coref = e.FlagsId < 0 && (coref_tallies == null || coref_tallies[~e.Mark] > 1);
				if (f_coref)
				{
					if (d == null)
						d = new Dictionary<Edge, int>();
					int cre;
					if (f_internal)
					{
						cre = e.Mark;
						if (!d.ContainsKey(e))
							d.Add(e, 1);
						else
							f_descend = false;
					}
					else
					{
						if (!d.TryGetValue(e, out cre))
							d.Add(e, cre = coref_tag++);
						else
							f_descend = false;
					}
					if (!f_internal)
						s += " #" + cre.ToString();
				}

				if (f_marks)
				{
					s += " " + e.Mark.ToString();
				}

				if (f_internal)
#if DEBUG
					s += " " + Edge.FormatMark(e);
#else
					s += " " + e.Mark.ToString();
#endif

				this.Add(s);
				if (f_descend && (e.FlagsId & Edge.Flag.EtmNonBareType) != 0)
				{
					/// this determines the order of coref tag numbering. it used to be specified here with 
					/// .OrderBy(cp => cp.IntroducedBy.m_level) but this is not needed so long as the feature index order
					/// in the type is kept this way, as it is at this time.
					foreach (int i_feat in ftm.rgrgfix_by_type[(int)(e.FlagsId & Edge.Flag.MultiIdMask)])
					{
						Edge q;
						if (restrictor != null && restrictor[i_feat])
							continue;

						if (tfs.TryGetEdge(i_feat, e.Mark, out q))
						{
							stack.Push(ftm[i_feat].feature.ToUpper());
							_list_paths(q);
							stack.Pop();
						}
					}
				}
			}

			public override string ToString() { return this.StringJoin(Environment.NewLine) + Environment.NewLine; }
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public class MonospaceFormatter
		{
			TypeUtils tu;
			FeatMgr ftm;
			Tfs tfs;
#if oc
			Dictionary<Edge, int> corefs = new Dictionary<Edge, int>();
#else
			ReentrancyFinder corefs;
#endif
			bool f_uncons_top = true;
			//bool f_unfill = true;
			//public int[] FeatureDisplayOrder { get; set; }
			bool f_PET_order;
			/// <summary>
			/// Type names can be substituted to account for e.g. different glb number assignments in a comparison system.
			/// </summary>
			IDictionary<String, String> type_name_remapping = null;
			public Restrictor r;

			public MonospaceFormatter(FeatMgr ftm, bool f_tops = true, bool f_PET_order = false, Restrictor r = null)
			{
				this.ftm = ftm;
				this.r = r ?? ftm.r_none;
				this.tu = ftm.fsu.tu;
				this.f_uncons_top = f_tops;
				this.f_PET_order = f_PET_order;
				//if (f_uncons_top)
				//	f_unfill = false;

#if DEBUG
#warning cleanup debugging code
				if (f_PET_order && MonospaceFormatter.pfi == null)
				{
					int i_feat;
					MonospaceFormatter.pfi = new int[ftm.Count];
					for (int i = 0; i < PET_feat_order.Length; i++)
						if ((i_feat = ftm.GetFeatureIndex(PET_feat_order[i])) >= 0)
							pfi[i_feat] = i;
				}
#endif
			}
			public static String Format(ITfsSlot ts, bool f_tops = true, bool f_PET_order = false, Restrictor r = null)
			{
				var tfs = ts.Tfs;
				return new MonospaceFormatter(tfs.ftm, f_tops, f_PET_order, r).Format(tfs, ts.SlotIndex);
			}

			/// <summary>
			/// Note: Single-threaded (use of corefs)
			/// </summary>
			public String Format(Tfs tfs, int ix1 = 0)
			{
				this.tfs = tfs;

#if oc
				corefs.Clear();
#else
				corefs = new ReentrancyFinder(new TfsSlot(tfs, ix1), 1, r);
#endif
				String s = _format_edge(0, ix1);
				if (s == String.Empty)
					return s;
				return new TfsSlot(tfs, ix1).Type.Name + Environment.NewLine + s;
			}

			String _format_edge(int indent, int ix1)
			{
				var rgcr = agree.SlotRefArray.GetConstraints(tfs, ix1, !f_uncons_top, r);

				if (rgcr.Length == 0)
					return String.Empty;

				//if (FeatureDisplayOrder != null)
				//	rgcr = rgcr.OrderBy(cr => Array.IndexOf<int>(FeatureDisplayOrder, cr.i_feat)).ToArray();
				//if (f_PET_order)
				//	Array.Sort(rgcr, (a, b) => pfi[a.i_feat] - pfi[b.i_feat]);

				var pk = rgcr.Select(x => Array.IndexOf(PET_feat_order, x.FEATURE)).ToArray();
				Array.Sort<int, TfsSlot>(pk, rgcr);

				int feat_max = rgcr.Max(cr => cr.FEATURE.Length) + 1;

				String s_indent = new String(' ', indent);
				StringBuilder sb = new StringBuilder();

				bool f_first = true;
				foreach (var cr in rgcr)
				{
					Edge ne = cr.Edge;
					if (f_first)
					{
						sb.Append(s_indent + "[ ");
						f_first = false;
					}
					else
						sb.Append("," + Environment.NewLine + s_indent + "  ");

					sb.Append(cr.FEATURE.PadRight(feat_max));

					//bool f_old_coref = false;
					String coref_text = String.Empty;
					if (ne.IsCoreferenced)
					{
#if oc
						int i_coref;
						if (!corefs.TryGetValue(ne, out i_coref))
						{
							i_coref = corefs.Count + 1;
							corefs.Add(ne, i_coref);
							coref_text = String.Format("#{0}:", i_coref);
						}
						else
						{
							coref_text = "#" + i_coref.ToString();
							f_old_coref = true;
						}
						sb.Append(coref_text);
#else
						ReentrancyFinder.Entry ce;
						if (corefs.TryGetValue(ne, out ce) && ce.Count > 1)
						{
							sb.Append("#" + ce.i_tag);
							if (ce.f_displayed)
								continue;
							sb.Append(":");
							ce.f_displayed = true;
						}
#endif
					}

					//if (!f_old_coref)
					{
						String s_type;
						if (ne.FlagsId.IsIntegerValue())
							s_type = "\"" + ne.FlagsId.GetIntegerValue().ToString() + "\"";
						else if (ne.FlagsId.IsStringFamilyValue())
							s_type = tu.TypeNameOrStringValueDisplay(ne.FlagsId);
						else
						{
							s_type = tu.GetEdgeType(ne.FlagsId).Name;
							String s_tmp;
							if (type_name_remapping != null && type_name_remapping.TryGetValue(s_type, out s_tmp))
								s_type = s_tmp;
						}

						sb.Append(s_type);

						//if (!f_unfill /*|| !CanUnfill(cr)*/)
						{
							String cons = _format_edge(indent + feat_max + 2 + coref_text.Length, cr.ix1);
							if (cons.Length > 0)
								sb.Append(Environment.NewLine + cons);
						}
					}
				}
				sb.Append(" ]");
				return sb.ToString();
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// 
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public bool CanUnfill(ConstraintRef cr)
			{
				Edge e = cr.Constraint;
				Type t0 = tu.GetEdgeType(e.FlagsId);

				foreach (int fix in t0.fc.rg_fix)
				{
					Edge ne;
					if (tfs.TryGetEdge(fix, e.Mark, out ne))
					{
						Edge ne2;
						tfs.TryGetEdge(fix, t0.Expanded._top_edge.Mark, out ne2);

						Type t3 = tu.GetEdgeType(ne2.FlagsId);


						if (ne.IsCoreferenced)
							return false;

						if (tu.GetEdgeType(ne.FlagsId) != t3)
							return false;

						if (ne.Mark != 0 && !CanUnfill(new ConstraintRef(tfs, e, fix)))
							return false;
					}
				}
				return true;
			}
			public static int[] pfi;
			public static String[] PET_feat_order =
			{
"FIRST","REST","LIST","LAST","ARGS","SYNSEM","KEY-ARG","LOCAL","CAT","HEAD","MINORS","MIN","CONT","HOOK","INDEX","--MIN","--SIND","ORTH","INFLECTD","GENRE","DIALECT",
"IDIOM","RNAME","FROM","C-CONT","ALTS","TOKENS","PHON","ONSET","--TL","LKEYS","KEYREL","CFROM","CTO","CLASS","TO","FORM","+LIST","+CLASS","+FROM","+FORM","+LAST",
"+TO","PUNCT","PNCTPR","LB","RB","ARG-S","NONLOC","OPT","LEX","MODIFD","REL","QUE","XARG","CONJ","AGR","SLASH","MOD","VAL","SPCMPS","COMPS","CTXT","PASSIVE","VPELLIP",
"ADVADD","ADVNGADD","NPPART","VADJ","NGER","SQPNCT","VPREF","DATIVE","ATTRJ","CSAI","MC","POSTHD","HC-LEX","HS-LEX","NEGPOL","DIGIT","-CTYPE-","PRD","AUX","INV",
"CASE","SUBJ","SPR","SPEC","KCMP","POSS","TAM","LSYNSEM","--BARE","VFORM","LPRED","ADDPN","ADDTAM","--ADDIN","PN","GEN","LPERIPH","RPERIPH","INSTLOC","SORT",
"--TPC","PNG","DIV","IND","PRONTYPE","PRF","PROGR","TENSE","ASPECT","MOOD","E","PERF","PROG","SF","PRED","LBL","LNK","ARG0","LTOP","--SLTOP","RELS","HCONS","HARG",
"LARG","CHEAD","CLEX","CREL","CPUNCT","CPNG","ACTIVATED","PRESUP","ALTMIN","NORM","ALTKEYREL","ALT2KEYREL","--COMPKEY","--OCOMPKEY","--+COMPKEY","--+OCOMPKEY","--COMPHD",
"--+ARGIND","NODE","LNAME","META-PREFIX","META-SUFFIX","IFORM","RSTR","BODY","ARG1","ARG2","CARG","ARG3","ARG4","L-INDEX","R-INDEX","L-HNDL","R-HNDL","CARDINAL",
"ARG","LPUNCT","RPUNCT","PAIRED","RCLSTR","RPAREN","RFP","PSF","LD","RD","+CARG","+TNT","+PRED","+TRAIT","+ID","+TICK","+UW","+IT","+LB","+RB","+LD","+RD",
"+HD","+TI","+LL","+TG","+TAG","+PRB","+MAIN","+TAGS","+PRBS","+INITIAL","+CASE","+CONTEXT","+INPUT","+OUTPUT","+POSITION","HD-DTR","NH-DTR","DTR1","DTR2",
"LCONJ-DTR","RCONJ-DTR","DTRA","DTRB","ND-AFF","DTR","--DT",

			//	"FIRST","REST","LIST","LAST","ARGS","SYNSEM","KEY-ARG","LOCAL","CAT","HEAD","MINORS","MIN","CONT","HOOK","INDEX","--MIN","--SIND","ORTH","INFLECTD","GENRE","DIALECT",
			//"IDIOM","RNAME","FROM","C-CONT","ALTS","TOKENS","PHON","ONSET","--TL","--CTYPE","--HID","LKEYS","KEYREL","CFROM","CTO","CLASS","TO","FORM","LB","RB","+LIST","+CLASS","+FROM",
			//"+FORM","+LB","+HD","+LL","+TG","+LAST","+TO","+RB","PUNCT","PNCTPR","ARG-S","NONLOC","OPT","LEX","MODIFD","REL","QUE","XARG","CONJ","AGR","SLASH","MOD",
			//"VAL","SPCMPS","COMPS","CTXT","PASSIVE","VPELLIP","ADVADD","ADVNGADD","NPPART","VADJ","NGER","SQPNCT","VPREF","DATIVE","ATTRJ","CSAI","MC","POSTHD","HC-LEX",
			//"HS-LEX","NEGPOL","DIGIT","-CTYPE-","PRD","AUX","CASE","SUBJ","SPR","SPEC","KCMP","POSS","TAM","LSYNSEM","--BARE","VFORM","INV","LPRED","ADDPN","ADDTAM","--ADDIN",
			//"PN","GEN","LPERIPH","RPERIPH","INSTLOC","SORT","--TPC","PNG","DIV","IND","PRONTYPE","PRF","PROGR","TENSE","ASPECT","MOOD","E","PERF","PROG","SF","PRED","LBL",
			//"LNK","ARG0","LTOP","--SLTOP","RELS","HCONS","HARG","LARG","CHEAD","CLEX","CREL","CPUNCT","CPNG","ACTIVATED","PRESUP","ALTMIN","NORM","ALTKEYREL","ALT2KEYREL",
			//"--COMPKEY","--OCOMPKEY","--+COMPKEY","--+OCOMPKEY","--COMPHD","--+ARGIND","NODE","LNAME","META-PREFIX","META-SUFFIX","IFORM","RSTR","BODY","ARG1","ARG2",
			//"CARG","ARG3","ARG4","L-INDEX","R-INDEX","L-HNDL","R-HNDL","CARDINAL","ARG","LPUNCT","RPUNCT","PAIRED","RCLSTR","RPAREN","RFP","PSF","+CARG","+TNT","+PRED","+TRAIT",
			//"+ID","+TICK","+UW","+IT","+TAG","+PRB","+MAIN","+TAGS","+PRBS","+INITIAL","+CASE","+CONTEXT","+INPUT","+OUTPUT","+POSITION","HD-DTR","NH-DTR","DTR1","DTR2",
			//"LCONJ-DTR","RCONJ-DTR","DTRA","DTRB","ND-AFF","DTR"
			};
		};
#if false

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		RootAnalysis ra;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public RootAnalysis Roots
		{
			get { return ra = ra ?? new RootAnalysis(this); }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public class RootAnalysis
		{
			Tfs tfs;
			TypeMgr tm;
			Dictionary<int, RootEdgeData> roots = new Dictionary<int, RootEdgeData>();

			public RootAnalysis(Tfs tfs)
			{
				this.tfs = tfs;
				this.tm = tfs.tm;
				foreach (Instance t in tm.g.em.AllInstances)
				{
					int mx_d;
					mx_d = t.Definition.TopEdge.Mark;
					if (mx_d != 0)
					{
						int mx_e = 0;
						if (t.IsExpanded)
						{
							if ((mx_e = t.Expanded.TopEdge.Mark) != 0 && mx_e != mx_d)
								roots.Add(mx_e, new RootEdgeData(t, RootEdgeType.Expanded, mx_e));
						}
						if (mx_d == mx_e)
							roots.Add(mx_d, new RootEdgeData(t, RootEdgeType.ExpandedDefinition, mx_d));
						else
							roots.Add(mx_d, new RootEdgeData(t, RootEdgeType.Definition, mx_d));
					}
				}
			}

			[Flags]
			public enum RootEdgeType
			{
				Unknown = 0,
				Definition = 1,
				Expanded = 2,
				ExpandedDefinition = 3,
				CycleDetected = 4,
			};

			[DebuggerDisplay("{ToString(),nq}")]
			public struct RootEdgeData
			{
				public RootEdgeData(Instance t, RootEdgeType ret, int mark)
				{
					this.t = t;
					this.ret = ret;
					this.mark = mark;
				}
				public Instance t;
				public RootEdgeType ret;
				public int mark;
#if DEBUG
				public override String ToString()
				{
					if (ret == RootEdgeType.Unknown || ret == RootEdgeType.CycleDetected)
						return ret.ToString();
					return String.Format("{0} {1} {2}", t.Name, ret, Edge.FormatMark(new Edge(0, mark)));
				}
#endif
			};

			[DebuggerDisplay("{ToString(),nq}")]
			public struct TaggedFsPath
			{
				public RootEdgeData red;
				public FsPath fsp;

				public override string ToString()
				{
					return String.Format("{0} {1}", red.ret == RootEdgeType.Unknown ? "???" : red.ToString(), fsp.ToString());
				}
			};
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public IEnumerable<Edge> TreeTops(IEnumerable<FeatMark> set)
		{
			HashSet<FeatMark> hs = new HashSet<FeatMark>(set);
			foreach (FeatMark fm in set)
				hs.ExceptWith(AllFeatMarks(fm.Constraint(this)));

			return hs._GroupBy(fm => fm.m)
				.Select(g =>
				{
					Type tx = tm.GetMaximalTypeForFeatures(g.Select(pm => pm.i_feat)) ?? tm.TopType;
					return new Edge(tx.EdgeFlag, g.Key);
				});
		}
#endif
		MemoizedItem<ReentrancyFinder> mrd;
		public Dictionary<Edge, ReentrancyFinder.Entry> Reentrancies()
		{
			if (mrd.FirstClaim())
			{
				var rx = new ReentrancyFinder(this);
				//foreach (var kvp in rx)
				//    if (kvp.Value.cref_display.i_feat==tm.g.mrsm.IndexPath.FirstIx)
				//        kvp.Value.cref_display=
				mrd.SetResult(rx);
			}
			return mrd.Result;
		}

		public int GetCorefTagNum(Edge e)
		{
			ReentrancyFinder.Entry rev;
			return e.FlagsId < 0 && Reentrancies().TryGetValue(e, out rev) ? rev.i_tag : -1;
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class ReentrancyFinder : Dictionary<Edge, ReentrancyFinder.Entry>
	{
		public ReentrancyFinder(ITfsSlot ts, int i_first_tag = 0, Restrictor r = null)
		{
			this.tfs = ts.Tfs;
			this.coref_tag = i_first_tag;
			this.r = r;
			this.rgrgfix = tfs.ftm.rgrgfix_by_type;

			start(ts.Edge());
		}

		public class Entry : HashSet<ConstraintRef>
		{
			public Entry(ConstraintRef cref)
			{
				this.Add(cref);
				this.i_tag = -1;
			}
			public int i_tag;
			public bool f_displayed;
			public override String ToString() { return String.Format("#{0} in-edges: {1}", i_tag, this.Count); }
		};

		Restrictor r;
		readonly Tfs tfs;
		readonly int[][] rgrgfix;
		int coref_tag;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void start(Edge e)
		{
			//MultiRootTfs rtfs = tfs as MultiRootTfs;
			//if (rtfs != null)
			//{
			//	for (int ix1 = 1; ix1 <= rtfs.Count; ix1++)
			//	{
			//		if ((e = rtfs.GetIx1Edge(ix1)).Mark != 0)
			//		{
			//			this.rgrgfix = tfs.GetIx1RmGgix(ix1).ftm.rgrgfix_by_type;
			//			_get_reentrancies(e);
			//		}
			//	}
			//}
			//else 

			var ftm = tfs.ftm;
			int i_feat;
			Tfs.MonospaceFormatter.pfi = new int[ftm.Count];
			for (int i = 0; i < Tfs.MonospaceFormatter.PET_feat_order.Length; i++)
				if ((i_feat = ftm.GetFeatureIndex(Tfs.MonospaceFormatter.PET_feat_order[i])) >= 0)
					Tfs.MonospaceFormatter.pfi[i_feat] = i;

			_get_reentrancies(e);

			if (tfs.mrsm == null)
				return;

#if false
			/// attempt to push substructure display into RELS
			//var hf = tfs.GetIx1TypeMgr(0).g.mrsm.hook_feats;
			var hf = tfs.mrsm.hook_feats;
			if (hf != null)
			{
				foreach (var ent in this.Values)
				{
					ConstraintRef cr = ent.First();
					ent.cref_display = ent.FirstAnyOrDefault(cc => Array.IndexOf<int>(hf, cc.i_feat) == -1);
				}
			}
#endif
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		unsafe void _get_reentrancies(Edge e)
		{
			/// this determines the order of coref tag numbering. it used to be specified here with 
			/// .OrderBy(cp => cp.IntroducedBy.m_level) but this is not needed so long as the feature index order
			/// in the type is kept this way, as it is at this time.
			foreach (int i_feat in rgrgfix[(int)(e.FlagsId & Edge.Flag.MultiIdMask)]
				.OrderBy(_f => Tfs.MonospaceFormatter.pfi[_f]))
			{
				Edge next_edge;
				if (tfs.TryGetEdge(i_feat, e.Mark, out next_edge))
				{
					if (r != null && r[i_feat])
						continue;
					if (next_edge.FlagsId < 0)
					{
						ConstraintRef cref = new ConstraintRef(tfs, e, i_feat);
						Entry cre;
						if (this.TryGetValue(next_edge, out cre))
						{
							if (cre.i_tag == -1)
								cre.i_tag = coref_tag++;
							cre.Add(cref);
							continue;
						}
						this.Add(next_edge, new Entry(cref));
					}
					if ((next_edge.FlagsId & Edge.Flag.EtmNonBareType) != 0)
						_get_reentrancies(next_edge);
				}
			}
		}
	};

	public static class _itfs_util_ext
	{
		public static String ToTextRow(this ITfsSlot _this)
		{
			return String.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}", field_strings(_this));
		}

		public static String ToHtmlTableRow(this ITfsSlot _this)
		{
			return String.Format(@"<tr>
	<td style='text-align:right;'>{0}</td>
	<td style='text-align:right;'>{1}</td>
	<td style='text-align:right;'>{2}</td>
	<td>{3}</td>
	<td>{4}</td>
	<td style='text-align:right;'>{5}</td>
</tr>", field_strings(_this));
		}

		static String[] field_strings(ITfsSlot _this)
		{
			var ix1 = _this.SlotIndex;
			var m = _this.out_mark();
			var arr = new String[6];
			arr[0] = ix1.ToString();
			arr[1] = ix1 == 0 ? "" : (ix1 - 1).ToString();
			arr[2] = ix1 == 0 ? "" : _this.in_mark().ToString();
			arr[3] = ix1 == 0 ? "" : _this.FEATURE();
			arr[4] = _this.TypeNameOrStringValueDisplay();
			arr[5] = m == 0 ? "⊣" : m.ToString();
			return arr;
		}
	};
}
